import {
  Component,
  OnInit,
  EventEmitter,
  ViewEncapsulation,
  ViewChild,
  AfterViewInit,
  OnChanges,
  OnDestroy,
  ChangeDetectionStrategy,
  ElementRef
} from '@angular/core';
import { ActivatedRoute } from "@angular/router";
import { Observable } from "rxjs/Observable";
import { Subject } from "rxjs/Subject";
import { Subscription } from "rxjs/Subscription";
import { QuoteTableService, TableState } from "./quote-table.service";
import { SocketService } from "./../../socket/socket.service";
import { LoadingService } from "./../../shared/loading.service";
import { DropdownModule } from "dropdown";
import { BroadcastService, BROADCAST_TYPE } from "broadcast";
import { getHeightOfElem } from "./utils";
import { StockDetail } from "utils";
import * as _ from "lodash";

import "./../../shared/rxjs-operators";

const SET_SIZE = 0,
  UPDATE_DATA = 1,
  SET_OFFSET = 2,
  SET_STRICK = 3;

const SORT_LIST = [
  "last_px",
  "business_balance",
  "business_amount",
  "px_change",
  "px_change_rate",
  "amplitude",
  "turnover_ratio",
  "vol_ratio",
  "pe_rate",
  "dyn_pb_rate",
  "circulation_value",
  "business_amount_in",
  "business_amount_out",
  "entrust_rate"
];


/**
 * 表头分为两部分，不需要某一部分留空
 * 
 * @export
 * @interface QuoteHeaders
 */
export interface QuoteHeaders {
  headHeader?: QuoteHeader[];
  bodyHeader?: QuoteHeader[];
}

/**
 * 表头接口,key表示字段名称，label表示所显示的标签
 * 
 * @export
 * @interface QuoteHeader
 */
export interface QuoteHeader {
  class?: string[];
  label: string;
  key: string;
  withUnit?: boolean;
  withUpOrDown?: boolean;
  withUp?: boolean;
  upDownWith?: string;
  withPercent?: boolean;
  startPoint?: number;
  miniUnit?: number;
  transform?: (...args) => any;
  appendModule?: {
    module: any;
    headerComponentName: string;
    cellComponentName: string;
  }
}

export class TableParams {
  method?: string;
  body?: any;
  routerId?: string;
  dataUrl: string;
  search?;
}

export interface TableOptions {
  subscribe?: {
    enabled?: boolean,
    channels?: any[]
  };
  minWidthOfBody?: boolean;
  loadByDynamic?: boolean;
  localSort?: string[],
  defaultLocalSort?: string,
  autoClickFirst?: boolean,
  afterGetData?: (data: StockDetail[], options: TableOptions) => any,
  selectedHighLight?: boolean;
  responsive?: {
    enabled?: boolean,
    rowHeight?: number
  };
  updateByTime?: boolean,
  parts?: {
    order?: boolean,
    emptyRow?: boolean,
    pagination?: boolean,
    caption?: boolean | string,
    sort?: boolean,
    header?: boolean,
  }
}

const getValues = (obj: Object) => Object.keys(obj).map(key => obj[key]);

const defaultTableOptions = {
  subscribe: {
    enabled: false,
    channels: []
  },
  minWidthOfBody: true,
  loadByDynamic: false,
  localSort: undefined,
  defaultLocalSort: undefined,
  autoClickFirst: true,
  selectedHighLight: true,
  responsive: {
    enabled: true,
    rowHeight: 30
  },
  parts: {
    order: false,
    emptyRow: true,
    pagination: true,
    caption: false,
    sort: true,
    header: true
  }
};

@Component({
  selector: 'quote-table',
  templateUrl: 'quote-table.component.html',
  inputs: [
    "updateEventSource",
    "headers",
    "options",
    "appendModule",
    "scrollable"
  ],
  outputs: [
    "quoteClick",
    "quoteDblClick"
  ],
  providers: [
    QuoteTableService, //每一个表格生成一个独立的QuoteService实例
    SocketService
  ],
  encapsulation: ViewEncapsulation.None,
  styleUrls: [
    "quote-table.component.less"
  ]
})
export class QuoteTableComponent implements OnInit, OnChanges, OnDestroy {
  updateEventSource; //触发更新数据的事件源；
  store; //与ScrollBar联动仓库；
  state = {

  };
  private scrollBarSource = new Subject<any>();
  _options;
  set options(customOptions) {
    this._options = _.merge({}, defaultTableOptions, customOptions);
    // this._options = Object.assign({}, defaultTableOptions, customOptions);
  }
  get options(): TableOptions {
    return this._options;
  }

  // 选中高亮 
  highLightStockSymbol: String;


  pagination = new Subject; //翻页器Subject
  _quoteData = []; //默认空数据数列
  get quoteData() {
    return this._quoteData;
  }
  set quoteData(data) {
    // console.debug("Quote Table:: ", data);
    if (this.options.subscribe.enabled && this.options.subscribe.channels) {
      this.options.subscribe.channels.forEach(channel => {
        this.socketSub.next({
          oldValues: this.quoteData.map(quote => quote.symbol),
          newValues: data.map(quote => quote.symbol)
        });
      });
    }
    this._quoteData = data;
  }
  fullData = [];
  @ViewChild("tableSet") tableSet;
  tableMinTop = 0;
  tableMinBottom = 0;
  defaultSort = "px_change_rate";
  sortSubject = new EventEmitter();
  socketSub = new Subject();
  socketHandler;
  subscription: Subscription;
  currentParams;
  // 排序
  currentSort;
  currentSortType;
  localSort;
  isAscent = 1;


  //单击某Quote是发出事件给父组件
  quoteClick = new EventEmitter<any>();

  quoteDblClick = new EventEmitter();

  //默认表头
  headers: QuoteHeaders = {
    headHeader: [
      {
        label: "名称",
        key: "name"
      },
      // {
      //   label: "",
      //   key: "",
      //   appendModule: {
      //     module: DropdownModule,
      //     headerComponentName: "DropdownComponent",
      //     cellComponentName: "DropdownComponent",
      //   }
      // },
      {
        label: "股票代码",
        key: "symbol",
      }
    ],
    bodyHeader: [
      {
        label: "现价",
        key: "current"
      }, {
        label: "涨跌幅",
        key: "change_rate",
        withUpOrDown: true,
        withPercent: true
      }, {
        label: "涨跌",
        key: "change_amount",
        withUpOrDown: true,
      }, {
        label: "成交量",
        key: "volume",
        withUnit: true,
      }, {
        label: "成交额",
        key: "balance",
        withUnit: true,
        // }, {
        //   label: "五分钟涨跌幅",
        //   key: "min5_change",
        //   withUpOrDown: true,
        //   withPercent: true
      }, {
        label: "换手率",
        key: "turnover_ratio",
        withPercent: true
      }, {
        label: "量比",
        key: "vol_ratio",
      }, {
        label: "委比",
        key: "entrust_rate",
      }, {
        label: "振幅",
        key: "amplitude",
      }, {
        label: "内盘",
        key: "amount_in",
      }, {
        label: "外盘",
        key: "amount_out",
      }, {
        label: "流通市值",
        key: "circulation_value",
        withUnit: true
      }, {
        label: "市盈率",
        key: "PE",
      }, {
        label: "市净率",
        key: "PB",
      }
    ]
  };

  constructor(
    private broadcast: BroadcastService,
    private router: ActivatedRoute, //当前路由
    private quoteTable: QuoteTableService, //当前表数据处理服务
    private loader: LoadingService, //表格Loading
    private socket: SocketService, //socket服务
    private elem: ElementRef
  ) { }

  setOrder(sortBy: string) {
    console.debug("SortBy: ", sortBy);
    if (!sortBy) return;
    // if (this.options.parts.sort)
    //   this.quoteTable.setOrder(sortBy);
    if (
      this.options.localSort
      && this.options.localSort.indexOf(sortBy) !== -1
    ) {
      this.localSort === sortBy
        ? (this.isAscent *= -1)
        : null;
      this.localSort = sortBy;
      return this.reloadTable(this.fullData);
    }
    this.localSort = undefined;
    this.currentSort === sortBy
      ? this.currentSortType === 0
        ? this.currentSortType = 1
        : this.currentSortType = 0
      : this.currentSortType = 1;
    console.debug("Current Sort: ", this.currentSort);
    this.sortSubject.next(sortBy);
  }

  onClick(quote) {
    // console.debug("Quote Table: ", quote);
    if (this.options.selectedHighLight && quote && quote.symbol) {
      this.highLightStockSymbol = quote.symbol;
    }
    this.quoteClick.emit(quote);
  }

  onDblClick(quote) {
    // console.debug("Quote Db Click: Origin:", quote);
    this.quoteDblClick.emit(quote);
  }

  getQuoteSymbol(quote) {
    // return this.quoteData;
    return quote.symbol;
  }

  /**
   * 初始化配置
   * @memberOf QuoteTableComponent
   */
  ngOnInit() {
    if (!this.options) {
      this.options = {}
    }
    let { options } = this;
    this.localSort = this.options.defaultLocalSort;
    // console.debug("TABLE OPTIONS: ", options);
    // 订阅QuoteTableService状态变化.
    // 映射状态Observable 为 SortedData Observable，检查两次是否相同，如果相同，不继续传递，
    // 如不同，则退订前一次，订阅新一次
    // 将排序后数据设置为本组件数据源
    // let source = options.parts.pagination ? this.pagination : this.quoteTable; //如果含有翻页器，数据经由翻页器过滤后再次传回table
    let source = this.quoteTable; //如果含有翻页器，数据经由翻页器过滤后再次传回table

    this.socketSub.debounceTime(200).subscribe(socketValues => {
      this.options.subscribe.channels.forEach(channel => {
        this.socket.emit("unsub", { channel, symbols: socketValues["oldValues"] });
        this.socket.emit("sub", { channel, symbols: socketValues["newValues"] });
      });
    });

    this.socketHandler = data => {
      console.error("DYSOCKET: ", data);
      this.fullData = this.fullData.map((v, i) => {
        return v.symbol in data
          ? {
            ...v,
            ...data[v.symbol]
          }
          : v;
      });
      this.reloadTable(this.fullData);
    };

    if (this.options.subscribe.enabled && this.options.subscribe.channels) {

      this.options.subscribe.channels.forEach(channel => {

        this.socket.on(channel, data => {
          this.fullData = this.fullData.map((v, i) => {
            return v.symbol in data
              ? {
                ...v,
                ...data[v.symbol]
              }
              : v;
          });
          this.reloadTable(this.fullData);
        })
      });
    }

    // 状态仓库，维护当前数据，及数据数量
    this.store =
      new Subject().scan(
        (state, action) => {
          switch (action["type"]) {
            case SET_SIZE:
              return Object.assign({}, state, {
                size: action["payload"],
                ticksToShow: state.sticky ?
                  state.ticks.slice(-action["payload"]) :
                  state.ticks.slice(Math.floor(state.offset * state.ticks.length), Math.floor(state.offset * state.ticks.length) + action["payload"])
                // ticksToShow: state.ticks.slice(Math.floor(state.offset * state.ticks.length), Math.floor(action["payload"] * state.offset) + action["payload"])
                // ticksToShow: state.ticks.slice(state.ticks.length - action["payload"])
              });
            case UPDATE_DATA:
              return Object.assign({}, state, {
                ticks: action["payload"],
                ticksToShow: state.sticky ?
                  action["payload"].slice(-state.size) :
                  action["payload"].slice(Math.floor(state.offset * state.ticks.length), Math.floor(state.offset * state.ticks.length) + state.size)
              });
            case SET_OFFSET:
              return Object.assign({}, state, {
                offset: action["payload"],
                sticky: action["sticky"],
                ticksToShow: state.ticks.slice(Math.floor(action["payload"] * state.ticks.length), Math.floor(action["payload"] * state.ticks.length) + state.size)
              });
            case SET_STRICK:
              return Object.assign({}, state, { sticky: action["payload"] });
            default:
              return state;
          }
        }, {
          size: 9,
          offset: 0,
          ticks: [],
          sticky: false
        });
    this.subscription =
      this.store.subscribe(state => {
        this.fullData = state.ticks;
        // 原ticks数据的ticksToShow未去重，此处去重
        state.ticks = this.deWeight(state.ticks);
        this.quoteData = state.ticksToShow;
        this.state = state;
        this.scrollBarSource.next({
          sticky: state.sticky,
          amountOfTotal: state.ticks.length,
          amountOfVisible: state.ticksToShow.length
        });
      });


    // 判断是否绑定了事件更新的Observable,如果没有则以路由参数为准
    if (!this.updateEventSource) {
      this.updateEventSource = this.router.params;
    }

    this.subscription.add(
      this.updateEventSource
        .switchMap(params => {
          // console.debug("Table Change: ", params);
          this.currentParams = params;
          this.currentSort = undefined;
          this.currentSortType = 1;
          this.loader.showLoading("table");
          return this.quoteTable.getDataByParams(
            this.getHttpParams(params)
          );
        })
        .subscribe((data, some) => {
          data = (data.data) ? data.data : data
          this.reloadTable(data, true);
        })
    );

    this.subscription.add(this.sortSubject.switchMap(
      sortBy => this.quoteTable.getDataByParams(
        this.getHttpParams(this.currentParams, sortBy as string)
      ))
      .subscribe(data => {
        this.reloadTable(data);
      })
    );

    // 五秒轮询重刷数据
    if (this.options.updateByTime) {
      this.subscription.add(
        Observable.interval(5000).switchMap(() =>
          this.quoteTable.getDataByParams(
            this.getHttpParams(this.currentParams, this.currentSort, this.currentSortType)))
          .subscribe(data => this.reloadTable(data))
      );
    }

    // 响应重绘事件
    this.subscription.add(
      this.broadcast.broadcast
        .filter(action => action.type === BROADCAST_TYPE.RENDER_CHAERT)
        .subscribe(action => {
          this.updatePageSize();
        })
    );

  }

  reloadTable(data, forceReload = false) {
    // format STOPT stock data to '--'
    if (!data.map)
        return;
    data = data.map(a => {
      if (a.status && (a.status === "STOPT" || a.status === "SUSP")) {
        a.current = "--";
        a.change_rate = "--";
        a.change_amount = "--";
      }
      return a;
    });

    this.loader.hideLoading("table");
    // 数据回调
    if (this.options.afterGetData !== undefined) {
      data = this.options.afterGetData.call(this, data, this.options);
    }
    // console.debug("Default Local Sort: ", this.localSort, this);
    // 本地排序
    let sortedData =
      this.localSort
        ? data.sort((first, second) => {
          if (isNaN(parseFloat(first[this.localSort]))) {
            return 1;
          }
          return (first[this.localSort] < second[this.localSort]
            ? 1
            : first[this.localSort] === second[this.localSort]
              ? 0
              : -1)
            * this.isAscent;
        })
        : data;
    if (this.options.parts.order) {
      // console.debug("This Table Has Order: ", this.options);
      sortedData.forEach((d, i) => d["$order"] = i + 1);
    }
    this.updatePageSize(); // 再次刷新尺寸

    this.store.next({
      type: UPDATE_DATA,
      payload: sortedData
    });
    if (sortedData.length && this.options.autoClickFirst && forceReload) {
      this.onClick(sortedData[0]);
      // this.quoteClick.emit(sortedData[0]);
    }
  }

  getHttpParams(params: TableParams, sortBy?: string, sortType?: string) {
    // console.debug("Quote Table GetHttpParams: ", params);
    if (!this.options.parts.sort) return params;
    let flag = sortBy || this.defaultSort;
    SORT_LIST.indexOf(sortBy) === -1 ? flag = this.defaultSort : 0;
    this.currentSort = flag;
    if (this.currentSortType !== undefined) {
      flag += `&sortType=${this.currentSortType}`;
    }

    let search = params.dataUrl.search(/\?/) === -1 ? "?sortBy=" : "&sortBy=";
    if (params.method === "post") {
      let body = Object.assign({}, params.body, {
        sortBy: this.currentSort,
        sortType: this.currentSortType
      });
      return Object.assign({}, params, { body });
    }
    return Object.assign({}, params, { dataUrl: params.dataUrl + search + flag });
  }

  updateVisibleData(pos) {
    // console.debug("Scroll Pos: ", pos);
    this.store.next({
      type: SET_OFFSET,
      payload: pos.pos,
      sticky: pos.sticky
    });
  }
  // Copy from pagination
  getPageSize() {
    // 待调整
    let tableContainerLook = this.elem.nativeElement.parentElement.getElementsByClassName("quote-table")[0].getBoundingClientRect();
    let tableMainLook = this.elem.nativeElement.parentElement.getElementsByClassName("quote-table-main")[0].getBoundingClientRect();
    let tableTop = tableMainLook.top;
    this.tableMinTop = Math.abs(tableContainerLook.top - tableTop);
    this.tableMinBottom = Math.abs(tableContainerLook.bottom - tableMainLook.bottom);
    let tableBottom = tableContainerLook.bottom;
    let validHeight = Math.abs(tableTop - tableBottom);
    let size = Math.floor(validHeight / (this.options.responsive.rowHeight - 0.1));
    return size;
  }

  // 股票数组去重
  deWeight(ticks) {
    let res = [];
    let json = [];
    ticks.forEach(d => {
      if (json.indexOf(d.name) === -1) {
        res.push(d);
        json.push(d.name);
      }
    });
    return res;
  }

  ngAfterViewInit() {
    setTimeout(() => this.updatePageSize());
  }

  /**
   * 待优化
   * 
   * 
   * @memberOf QuotePaginationComponent
   */
  updatePageSize() {
    this.store.next({
      type: SET_SIZE,
      payload: this.getPageSize()
    })
  }

 

  ngOnChanges(change) {
    // console.debug("Table CHange: ", change);
  }

  ngOnDestroy() {
    // console.debug("Scription: ", this.subscription);
    if (this.subscription) {
      this.subscription.unsubscribe();
    }

    if (this.socketHandler) {
      if (this.options.subscribe.enabled && this.options.subscribe.channels) {
        this.options.subscribe.channels.forEach(channel => this.socket.off(channel, this.socketHandler));
      }
    }
  }

}